﻿using System;

namespace Microsoft.Xna.Framework
{
	/// <summary>
	/// Defines an interface for game components.
	/// </summary>
    public interface IGameComponent
    {
		/// <summary>
		/// Called when the component should be initialized. This method can be used for tasks like querying for services the component needs and setting up non-graphics resources.
		/// </summary>
        void Initialize();
    }

    /// <summary>
	/// Base class for all XNA Framework game components.
    /// </summary>
    public class GameComponent : IGameComponent, IUpdateable, IDisposable
	{
		#region Fields
		Game _game;
        int _updateOrder;
        bool _enabled;
		#endregion

		#region Properties
		/// <summary>
		/// Gets the Game associated with this GameComponent.
		/// </summary>
		public Game Game
		{
			get { return _game; }
		}

		/// <summary>
		/// Indicates whether GameComponent.Update should be called when Game.Update is called.
		/// </summary>
		public bool Enabled
		{
			get { return _enabled; }
			set
			{
				if (_enabled != value)
				{
					_enabled = value;

					OnEnabledChanged(this, EventArgs.Empty);
				}
			}
		}
		/// <summary>
		/// Indicates the order in which the GameComponent should be updated relative to other GameComponent instances. Lower values are updated first.
		/// </summary>
		public int UpdateOrder
		{
			get { return _updateOrder; }
			set
			{
				if (_updateOrder != value)
				{
					_updateOrder = value;

					OnUpdateOrderChanged(this, EventArgs.Empty);
				}
			}
		}
		#endregion

		#region Events
		/// <summary>
		/// Raised when the GameComponent is disposed.
		/// </summary>
		public event EventHandler<EventArgs> Disposed;

		/// <summary>
		/// Raised when the Enabled property changes.
		/// </summary>
		public event EventHandler<EventArgs> EnabledChanged;

		/// <summary>
		/// Raised when the UpdateOrder property changes.
		/// </summary>
		public event EventHandler<EventArgs> UpdateOrderChanged;
		#endregion

		#region Constructor
		/// <summary>
        /// Initializes a new instance of this class.
        /// </summary>
        /// <param name="game">Game that the game component should be attached to.</param>
        public GameComponent(Game game)
        {
            _game = game;
            Enabled = true;
        }

		/// <summary>
		/// Allows a GameComponent to attempt to free resources and perform other cleanup operations before garbage collection reclaims the GameComponent.
		/// </summary>
		~GameComponent()
		{
			Dispose(false);
		}
        #endregion

		#region Protected Methods
		/// <summary>
		/// Releases the unmanaged resources used by the GameComponent and optionally releases the managed resources.
		/// </summary>
		/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
		protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				lock (this)
				{
					if (this.Game != null)
					{
						this.Game.Components.Remove(this);
					}
					EventHandler<EventArgs> handler = this.Disposed;
					if (handler != null)
					{
						handler(this, EventArgs.Empty);
					}
				}
			}
		}

		/// <summary>
		/// Called when the Enabled property changes. Raises the EnabledChanged event.
		/// </summary>
		/// <param name="sender">The GameComponent.</param>
		/// <param name="args">Arguments to the EnabledChanged event.</param>
		protected virtual void OnEnabledChanged(object sender, EventArgs args)
		{
		}
		/// <summary>
		/// Called when the UpdateOrder property changes. Raises the UpdateOrderChanged event.
		/// </summary>
		/// <param name="sender">The GameComponent.</param>
		/// <param name="args">Arguments to the UpdateOrderChanged event.</param>
		protected virtual void OnUpdateOrderChanged(object sender, EventArgs args)
		{
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Immediately releases the unmanaged resources used by this object.
		/// </summary>
		public void Dispose()
        {
			Dispose(true);
			GC.SuppressFinalize(this);
        }

		/// <summary>
		/// 
		/// </summary>
        public virtual void Initialize()
        {
        }
        
        /// <summary>
        /// Called when the GameComponent needs to be updated. Override this method with component-specific update code.
        /// </summary>
        /// <param name="gameTime">Time elapsed since the last call to Update</param>
        public virtual void Update(GameTime gameTime)
        {
        }
        #endregion
    }
}
